<?php

namespace backend\components;

use Yii;
use yii\base\Component;

/**
 * 下载证书|小微商户
 * Class Certificate
 * @package backend\components
 */
class Certificate extends Component
{
    //服务商号
    protected $mch_id;

    // 商户自定义key
    protected $diy_key;

    protected $downUrl = 'https://api.mch.weixin.qq.com/risk/getcertficates';

    protected $sslCertPath = '/data/wwwroot/default/teach/teach/components/cert/apiclient_cert.pem';

    protected $sslKeyPath = '/data/wwwroot/default/teach/teach/components/cert/apiclient_key.pem';

    public function __construct()
    {
        parent::__construct();

        $this->mch_id = Yii::$app->params['serviceNum']['mchId'];

        $this->diy_key = Yii::$app->params['serviceNum']['md5Key'];
    }

    /**
     * getRandChar 获取随机字符串
     * @param int $length
     * @return null|string
     */
    protected function getRandChar($length = 32)
    {
        $str = NULL;
        $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
        $newStr = str_shuffle($strPol);
        $max = strlen($strPol) - 1;
        for ($i = 0; $i < $length; $i++) {
            $str .= $newStr[mt_rand(0, $max)];    // rand($min,$max)生成介于min和max两个数之间的一个随机整数
        }
        return $str;
    }

    /**
     * MakeSign 生成签名
     * @param $data
     * @param string $signType
     * @return string
     */
    protected function makeSign(array $data, $signType = 'HMAC-SHA256')
    {
        //签名步骤一：按字典序排序参数
        ksort($data);
        $string = $this->toUrlParams($data);
        //签名步骤二：在string后加入KEY
        $string = $string . "&key=" . $this->diy_key;
        //签名步骤三：MD5加密或者HMAC-SHA256
        if ($signType == 'md5') {
            //如果签名小于等于32个,则使用md5验证
            $string = md5($string);
        } else {
            //是用sha256校验
            $string = hash_hmac("sha256", $string, $this->diy_key);
        }
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * ToUrlParams     格式化参数格式化成url参数
     * @param $data
     * @return string
     */
    protected function toUrlParams(array $data)
    {
        $buff = "";
        foreach ($data as $k => $v) {
            if ($k != "sign" && $v !== "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }
        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     * 输出xml字符
     * @param $data
     * @return string
     * @throws \Exception
     */
    protected function toXml($data)
    {
        if (!is_array($data) || count($data) <= 0) {
            throw new \Exception('xml解析错误');
        }
        $xml = "<xml>";
        foreach ($data as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * xml转数组
     * @param $xml
     * @return mixed
     */
    public function toArray($xml)
    {
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $xmlString = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        $val = json_decode(json_encode($xmlString), true);
        return $val;
    }

    /**
     * @param $url
     * @param string $data
     * @param array $headers
     * @param bool $userCert
     * @param int $timeout
     * @return array
     */
    protected function httpsRequest($url, $data = '', array $headers = [], $userCert = false, $timeout = 30)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        if (!empty($data)) {
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        //设置超时
        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
        if ($userCert) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);//严格校验
            curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM');
            curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($curl, CURLOPT_SSLCERT, $this->sslCertPath);
            curl_setopt($curl, CURLOPT_SSLKEY, $this->sslKeyPath);
        } else {
            if (substr($url, 0, 5) == 'https') {
                curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 信任任何证书
                curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); // 检查证书中是否设置域名
            }
        }
        if (!empty($headers)) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
            // curl_setopt($curl, CURLINFO_HEADER_OUT, true); //TRUE 时追踪句柄的请求字符串，从 PHP 5.1.3 开始可用。这个很关键，就是允许你查看请求header
            // $headers = curl_getinfo($curl, CURLINFO_HEADER_OUT); //官方文档描述是“发送请求的字符串”，其实就是请求的header。这个就是直接查看请求header，因为上面允许查看
        }
        curl_setopt($curl, CURLOPT_HEADER, true);    // 是否需要响应 header
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($curl);
        $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);    // 获得响应结果里的：头大小
        $response_header = substr($output, 0, $header_size);    // 根据头大小去获取头信息内容
        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);    // 获取响应状态码
        $response_body = substr($output, $header_size);
        $error = curl_error($curl);
        curl_close($curl);

        return [$response_body, $http_code, $response_header, $error];
    }

    public function curl($param = "", $url)
    {
        $postUrl = $url;
        $curlPost = $param;
        $ch = curl_init();                   //初始化curl
        curl_setopt($ch, CURLOPT_URL, $postUrl);         //抓取指定网页
        curl_setopt($ch, CURLOPT_HEADER, 0);          //设置header
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);      //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_POST, 1);           //post提交方式
        curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);      // 增加 HTTP Header（头）里的字段
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);    // 终止从服务端进行验证
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_SSLCERT, $this->sslCertPath); //这个是证书的位置
        curl_setopt($ch, CURLOPT_SSLKEY, $this->sslKeyPath); //这个也是证书的位置
        $data = curl_exec($ch);                 //运行curl
        curl_close($ch);
        return $data;
    }

    /**
     * downloadCertificates 2.0
     * @return mixed
     * @author   liuml  <liumenglei0211@163.com>
     * @DateTime 2019-03-05  11:03
     */
    public function downloadCertificates()
    {
        $data = [
            'mch_id' => $this->mch_id,
            'nonce_str' => $this->getRandChar(),
            'sign_type' => 'HMAC-SHA256',
            'sign' => '',
        ];
        $data['sign'] = $this->makeSign($data, $data['sign_type']);
        $url = $this->downUrl;
        $xml = $this->toXml($data);
        // 发起入驻申请请求
        //$result = $this->httpsRequest($url, $xml, [], true);
        $result = $this->curl($xml,$url);
        // 处理返回值
        return $this->toArray($result);
    }

}